package com.tvunetworks.center.user.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tvunetworks.center.common.constant.LocalConstant;
import com.tvunetworks.center.common.model.vo.PeerIdWithName;
import com.tvunetworks.center.common.util.Result;
import com.tvunetworks.center.common.util.ResultUtil;
import com.tvunetworks.center.gs.model.Device;
import com.tvunetworks.center.user.constant.UserConstant;
import com.tvunetworks.center.user.mapper.GridEventMapper;
import com.tvunetworks.center.user.mapper.UserDeviceMapper;
import com.tvunetworks.center.user.model.GridEvent;
import com.tvunetworks.center.user.model.User;
import com.tvunetworks.center.user.model.param.api.*;
import com.tvunetworks.center.user.model.result.ResultConstant;
import com.tvunetworks.center.user.model.vo.GridEventVo;
import com.tvunetworks.center.user.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @program: tvucc-aggregator
 * @description: GridEventServiceImpl
 * @author lebronchen
 * @create: 2020-01-03 13:34
 **/
@Service
@Slf4j
public class GridEventServiceImpl extends BaseService<GridEvent> implements GridEventService {

    @Autowired
    private DeviceService deviceService;
    @Autowired
    private UserDeviceService userDeviceService;
    @Autowired
    private UserService userService;
    @Autowired
    private UserDeviceMapper userDeviceMapper;
    @Autowired
    private GridEventMapper gridEventMapper;
    @Autowired
    private UserHttpService userHttpService;
    @Autowired
    private GridEventBindRecordService gridEventBindRecordService;
    @Autowired
    private GridEventRecordService gridEventRecordService;

    @Override
    @Transactional
    public Result<String> addEvent(AddGridEventParam param) {
        param.formatParam();
        Result<String> checkParamResult = this.checkAddEventParam(param);
        if (!checkParamResult.assertSuccess()) {
            return checkParamResult;
        }
        User user = userService.getByEmail(param.getEmail());
        GridEvent gridEvent = new GridEvent();
        BeanUtils.copyProperties(param, gridEvent);
        gridEvent.setStatus(UserConstant.GridEvent.Status.NOT_START)
                .setPaired(0)
                .setBindStatus(UserConstant.GridEvent.BindStatus.INIT)
                .setUserId(user.getId());
        super.insert(gridEvent);
        gridEventRecordService.createRecord(gridEvent.getId(), UserConstant.GridEvent.RecordStatus.CREATE);
        return ResultUtil.success();
    }

    private Result<String> checkAddEventParam(AddGridEventParam param) {
        // 校验 taskId 是否存在
        String taskId = param.getTaskId();
        GridEvent event = this.getByTaskId(taskId);
        if (event != null) {
            return ResultConstant.GridEvent.EVENT_TASKID_EXIST;
        }

        // 校验 startTime endTime prePairTime
        long startTime = param.getStartTime().longValue();
        long endTime = param.getEndTime().longValue();
        long prePareTime = param.getPrePairTime().longValue();
        if (prePareTime < System.currentTimeMillis()) {
            return ResultConstant.GridEvent.PRE_PAIR_TIME_MUST_BIGGER_THAN_NOW;
        }
        if (endTime < startTime) {
            return ResultConstant.GridEvent.END_TIME_MUST_BIGGER_THAN_START_TIME;
        }
        if (endTime < prePareTime) {
            return ResultConstant.GridEvent.PREPARE_TIME_CAN_NOT_BIGGER_THAN_ENT_TIME;
        }
        if (prePareTime > startTime) {
            return ResultConstant.GridEvent.PREPARE_TIME_CAN_NOT_BIGGER_THAN_START_TIME;
        }

        // 校验 grid 是否存在
        String gridPeerId = param.getGridPeerId();
        Device grid = deviceService.getCurrentDevice(gridPeerId);
        if (grid == null) {
            return ResultConstant.GridEvent.GRID_NOT_EXIST;
        }
        // 校验 user 是否存在
        String email = param.getEmail();
        User user = userService.getByEmail(email);
        if (user == null) {
            return ResultConstant.GridEvent.USER_NOT_EXIST;
        }
        String role = user.getRole();
        if (!(LocalConstant.UserRole.ADMIN.equals(role) || LocalConstant.UserRole.USER.equals(role))){
            // 非 Adam 和 User 建立任务
            return ResultConstant.GridEvent.USER_CAN_NOT_ADD_EVENT;
        }
        // 校验 receiver 是否存在
        String receiverPeerId = param.getReceiverPeerId();
        Device receiver = deviceService.getCurrentDevice(receiverPeerId);
        if (receiver == null) {
            return ResultConstant.GridEvent.RECEIVER_NOT_EXIST;
        }
        // 校验 userReceiver 是否存在
        boolean userDeviceExist = userDeviceService.isUserDeviceExist(user.getId(), receiverPeerId);
        if (!userDeviceExist) {
            return ResultConstant.GridEvent.RECEIVER_NOT_BELONG_USER;
        }
        return ResultUtil.success();
    }

    @Override
    @Transactional
    public Result<String> updateEvent(UpdateGridEventParam param) {
        Result<String> checkParamResult = this.checkUpdateEventParam(param);
        if (!checkParamResult.assertSuccess()) {
            return checkParamResult;
        }
        GridEvent event = this.getByTaskId(param.getTaskId());
        BeanUtils.copyProperties(param, event);
        super.updateSelective(event);
        return ResultUtil.success();
    }

    private Result<String> checkUpdateEventParam(UpdateGridEventParam param) {
        // 校验 taskId 是否存在
        String taskId = param.getTaskId();
        GridEvent event = this.getByTaskId(taskId);
        if (event == null) {
            return ResultConstant.GridEvent.EVENT_NOT_EXIST;
        }
        int status = event.getStatus().intValue();
        // 校验 event 是否结束
        if (status == UserConstant.GridEvent.Status.END) {
            return ResultConstant.GridEvent.EVENT_ENDED_CAN_NOT_UPDATE;
        }
        // 校验 event 是否取消
        if (status == UserConstant.GridEvent.Status.CANCEL) {
            return ResultConstant.GridEvent.EVENT_CANCELED_CAN_NOT_UPDATE;
        }

        // 校验 endTime prePairTime
        Long pre = param.getPrePairTime();
        if (pre != null) {
            // 已经配对的任务 不能修改预配对时间
            if (event.getPaired() == 1) {
                return ResultConstant.GridEvent.ALREADY_PAIRED_CAN_NOT_UPDATE_PREPAIR_TIME;
            }
            // 已经开始的任务 不能修改预配对时间
            if (status == UserConstant.GridEvent.Status.START) {
                return ResultConstant.GridEvent.EVENT_STARTED_CAN_NOT_UPDATE_PREPAIR_TIME;
            }
            event.setPrePairTime(pre);
        }
        Long start = param.getStartTime();
        if (start != null) {
            // 已经开始的任务 不能修改预开始时间
            if (status == UserConstant.GridEvent.Status.START) {
                return ResultConstant.GridEvent.EVENT_STARTED_CAN_NOT_UPDATE_START_TIME;
            }
            event.setStartTime(start);
        }

        Long end = param.getEndTime();
        if (end != null) {

            if (end < System.currentTimeMillis()) {
                return ResultConstant.GridEvent.END_TIME_MUST_BIGGER_THAN_NOW;
            }
            event.setEndTime(end);
        }

        // 校验 startTime endTime prePairTime
        long startTime = event.getStartTime().longValue();
        long endTime = event.getEndTime().longValue();
        long prePareTime = event.getPrePairTime().longValue();
        if (endTime < startTime) {
            return ResultConstant.GridEvent.END_TIME_MUST_BIGGER_THAN_START_TIME;
        }
        if (endTime < prePareTime) {
            return ResultConstant.GridEvent.PREPARE_TIME_CAN_NOT_BIGGER_THAN_ENT_TIME;
        }
        if (prePareTime > startTime) {
            return ResultConstant.GridEvent.PREPARE_TIME_CAN_NOT_BIGGER_THAN_START_TIME;
        }
        return ResultUtil.success();
    }

    @Override
    @Transactional
    public Result<String> cancelEvent(CancelGridEventParam param) {
        Result<String> checkParamResult = this.checkCancelEventParam(param);
        if (!checkParamResult.assertSuccess()) {
            return checkParamResult;
        }
        GridEvent event = this.getByTaskId(param.getTaskId());
        terminationEvent(event, UserConstant.GridEvent.Status.CANCEL, false);
        gridEventRecordService.createRecord(event.getId(), UserConstant.GridEvent.RecordStatus.CANCEL);
        return ResultUtil.success();
    }



    private Result<String> checkCancelEventParam(CancelGridEventParam param) {
        // 校验 taskId 是否存在
        String taskId = param.getTaskId();
        GridEvent event = this.getByTaskId(taskId);
        if (event == null) {
            return ResultConstant.GridEvent.EVENT_NOT_EXIST;
        }
        int status = event.getStatus().intValue();
        // 校验 event 是否结束
        if (status == UserConstant.GridEvent.Status.END) {
            return ResultConstant.GridEvent.EVENT_ENDED_CAN_NOT_CANCEL;
        }
        // 校验 event 是否取消
        if (status == UserConstant.GridEvent.Status.CANCEL) {
            return ResultConstant.GridEvent.EVENT_CANCELED_CAN_NOT_CANCEL;
        }
        return ResultUtil.success();
    }

    @Override
    @Transactional
    public Result<String> deleteEvent(DeleteGridEventParam param) {
        Result<String> checkParamResult = this.checkDeleteEventParam(param);
        if (!checkParamResult.assertSuccess()) {
            return checkParamResult;
        }
        GridEvent event = this.getByTaskId(param.getTaskId());
        terminationEvent(event, 0, true);
        gridEventRecordService.createRecord(event.getId(), UserConstant.GridEvent.RecordStatus.DELETE);
        return ResultUtil.success();
    }

    private Result<String> checkDeleteEventParam(DeleteGridEventParam param) {
        // 校验 taskId 是否存在
        String taskId = param.getTaskId();
        GridEvent event = this.getByTaskId(taskId);
        if (event == null) {
            return ResultConstant.GridEvent.EVENT_NOT_EXIST;
        }
        return ResultUtil.success();
    }

    @Override
    public GridEventVo getEventByTaskId(GetGridEventByTaskIdParam param) {
        return gridEventMapper.getEventVoByTaskId(param.getTaskId());
    }

    @Override
    public PageInfo<GridEventVo> pageEvent(GetGridEventParam param) {
        PageHelper.startPage(param.getPageNum(), param.getPageSize());
        if (param.getStatus() != null && param.getStatus().size() == 0) {
            return new PageInfo<>(new ArrayList<>());
        }
        List<GridEventVo> list = gridEventMapper.listEventVo(param);
        return new PageInfo<>(list);
    }

    @Override
    public PageInfo<PeerIdWithName> pageUserReceiver(GetUserReceiverParam param) {
        User user = userService.getByEmail(param.getEmail());
        if (user == null) {
            return new PageInfo<>(new ArrayList<>());
        }
        param.setUserId(user.getId());
        PageHelper.startPage(param.getPageNum(), param.getPageSize());
        List<PeerIdWithName> list = userDeviceMapper.listUserReceiver(param);
        return new PageInfo<>(list);
    }

    @Override
    public GridEvent getById(String id) {
        return super.selectExistByPrimaryKey(id);
    }

    @Override
    public GridEvent getByTaskId(String taskId) {
        GridEvent record = new GridEvent().setTaskId(taskId);
        return super.selectExistOne(record);
    }

    @Override
    public GridEvent getByIdContainDeleted(String id) {
        return super.selectByPrimaryKey(id);
    }

    @Override
    @Transactional
    public void prePairTask() {
        // 查询到达 prePairTime 时间，还没开始的任务
        List<Integer> statusList = Arrays.asList(new Integer[]{UserConstant.GridEvent.Status.NOT_START, UserConstant.GridEvent.Status.START});
        List<GridEvent> list = gridEventMapper.listNeedPairEvent(statusList, System.currentTimeMillis());
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        for (GridEvent event : list) {
            String receiverPeerId = event.getReceiverPeerId();
            String gridPeerId = event.getGridPeerId();

            // 去 GCS 添加配对关系
            Result<String> result = userHttpService.addPairFromGCS(receiverPeerId, gridPeerId);
            log.error("Grid event prePairTask call GCS to addPairFromGCS, event:[{}] result:[{}]", event, result);
            if (result.assertSuccess()) {
                // 配对成功，设置配对状态和 pairId
                event.setPaired(1);
                event.setPairId(Integer.valueOf(result.getResult()));
                event.setPairFailedReason(null);
                gridEventRecordService.createRecord(event.getId(), UserConstant.GridEvent.RecordStatus.PAIR);
            } else {
                // 配对失败，设置失败原因
                event.setPairFailedReason(result.getResult());
            }
            super.update(event);
        }
    }

    @Override
    @Transactional
    public void eventStartTask() {
        // 查询到达 startTime 时间，还没开始的任务
        List<GridEvent> list = gridEventMapper.listNeedStartEvent(UserConstant.GridEvent.Status.NOT_START, System.currentTimeMillis());
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        for (GridEvent event : list) {
            String userId = event.getUserId();
            String gridPeerId = event.getGridPeerId();
            event.setStatus(UserConstant.GridEvent.Status.START);
            boolean userDeviceExist = userDeviceService.isUserDeviceExist(userId, gridPeerId);
            boolean gridEventUserDeviceExist = true;
            if (userDeviceExist) {
                // 当前存在的通过 GridEvent 产生该 UserDevice 的 event 数量
                int count = gridEventBindRecordService.countUserDeviceExistBindRecord(userId, gridPeerId);
                gridEventUserDeviceExist = count == 0;
            }

            if (userDeviceExist) {
                // UserDevice 已经存在
                if (gridEventUserDeviceExist) {
                    // 存在 GridEvent 任务添加的 UserDevice
                    // 在任务开始之前，用户已经拥有通过 GridEvent 添加的这个 Grid，所以不需要再分配给用户，但是需要设置为 3:由任务开始产生的绑定，任务结束的时候需要移除绑定关系
                    event.setBindStatus(UserConstant.GridEvent.BindStatus.EVENT_START_ADD_BIND);
                } else {
                    // 不存在 GridEvent 任务添加的 UserDevice
                    // 在任务开始之前，用户已经拥有这个 Grid，但是不是 GridEvent 添加的，所以不需要再分配给用户，并且设置为 2:任务开始时已经有绑定关系，不需要处理
                    event.setBindStatus(UserConstant.GridEvent.BindStatus.ALREADY_BINDED);
                }
            } else {
                // UserDevice 不存在
                // 把 Grid 分配给用户，保存 UserDevice 绑定记录，并设置为 3:由任务开始产生的绑定，任务结束的时候需要移除绑定关系
                gridEventBindRecordService.addUserDevice(event.getId(), userId, gridPeerId);
                event.setBindStatus(UserConstant.GridEvent.BindStatus.EVENT_START_ADD_BIND);
            }
            gridEventRecordService.createRecord(event.getId(), UserConstant.GridEvent.RecordStatus.START);
            super.update(event);
        }
    }

    @Override
    @Transactional
    public void eventEndTask() {
        // 查询到达 EndTime 时间，还没结束的任务
        List<GridEvent> list = gridEventMapper.listNeedEndEvent(UserConstant.GridEvent.Status.START, System.currentTimeMillis());
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        for (GridEvent event : list) {
            log.error("Grid event eventEndTask call terminationEvent, event:[{}]", event);
            terminationEvent(event, UserConstant.GridEvent.Status.END, false);
            gridEventRecordService.createRecord(event.getId(), UserConstant.GridEvent.RecordStatus.END);
        }
    }

    /**
     * 查询有多少正在运行，并且使用当前 pairId 的 event
     * @param pairId
     * @return
     */
    private int countActiveEventByPairId(int pairId) {
        List<Integer> statusList = Arrays.asList(new Integer[]{UserConstant.GridEvent.Status.NOT_START, UserConstant.GridEvent.Status.START});
        return gridEventMapper.countActiveEventByPairId(pairId, statusList);
    }

    private void removePair(GridEvent event){
        Integer paired = event.getPaired();
        log.error("Grid event removePair start, event:[{}]", event);
        if (paired == 0) {
            log.error("Grid event removePair skip, event paired is 0, event:[{}]", event);
            // 没有配对 返回
            return;
        }

        Integer pairId = event.getPairId();
        int eventCount = this.countActiveEventByPairId(pairId);
        if (eventCount > 1) {
            // 有其他 GridEvent 也在使用当前 parId，那么不需要去删除配对 返回
            log.error("Grid event removePair skip, exist more than one event use this pairId, do noe need to remove, event:[{}] eventCount:[{}]", event, eventCount);
            return;
        }
        Result<String> result = userHttpService.removePairFromGCS(event.getPairId());
        log.error("Grid event removePair call GCS to remove pair, , event:[{}] result:[{}]", event, result);
        if (!result.assertSuccess()) {
            log.error("removePairFromGCS event:[{}] result:[{}]", event, result);
        }
    }

    /**
     * 终止 GridEvent   处理 pair、bindStatus
     * @param event
     * @param status end、cancel、delete
     */
    private void terminationEvent(GridEvent event, int status, boolean deleted){
        // 移除配对关系
        this.removePair(event);
        event.setPaired(0);
        String userId = event.getUserId();
        String gridPeerId = event.getGridPeerId();
        int bindStatus = event.getBindStatus().intValue();
        if (UserConstant.GridEvent.BindStatus.EVENT_START_ADD_BIND == bindStatus) {
            // 由任务开始产生的绑定，任务结束的时候需要移除绑定关系
            boolean userDeviceExist = userDeviceService.isUserDeviceExist(userId, gridPeerId);
            if (userDeviceExist) {
                // UserDevice 存在
                gridEventBindRecordService.removeUserDevice(event.getId(), userId, gridPeerId);
                // 4:任务结束，由2状态，解除绑定关系，变成3状态
                event.setBindStatus(UserConstant.GridEvent.BindStatus.EVENT_END_REMOVE_BIND);
            } else {
                // UserDevice 不存在
                // 5:由任务产生的绑定，任务结束移除绑定关系时，设备已经提前被移除
                event.setBindStatus(UserConstant.GridEvent.BindStatus.EVENT_END_NOT_BINDED);
            }
        }
        if (deleted) {
            // 删除的情况下，不设置 status
            event.setDeleteFlag(1);
        } else {
            event.setStatus(status);
        }
        super.update(event);
    }
}
